#include "spriteViewerProgram.h"

#include "spriteViewerOverlay.h"
#include "hardcodedMode.h"
#include "dataMode.h"
#include "spriteViewerMessages.h"

#include <fram/message.h>

#include <file/packedAccessor.h>

#include <shad/resourceHandler.h>

#include <util/indexMap.h>

#include <fill/drawContext.h>

#include <font/assetProcessor.h>
#include <font/assetContainer.h>
#include <font/drawContext.h>
#include <font/resourceHandler.h>

#include <spri/drawContext.h>

#include <imag/assetProcessor.h>
#include <imag/assetContainer.h>
#include <imag/resourceHandler.h>

#include <pack/packageWalker.h>

#include <util/binaryReader.h>

#include <inpu/mouseReader.h>
#include <inpu/mouseCursor.h>
#include <inpu/touchReader.h>
#include <inpu/touchCursor.h>

#include <foun/debug.h>

/*
 * Internal Constants
 */
static const s16 OVERLAY_TOUCH_ACTIVATE_SIZE = 100;

/*
 * Public Instance
 */
SpriteViewerProgram::SpriteViewerProgram()
: Program()
, m_pAccessor(NULL)
, m_pModeAccessor(NULL)
, m_pShaderResourceHandler(NULL)
, m_pImageAssetContainer(NULL)
, m_pImageAssetProcessor(NULL)
, m_pImageResourceHandler(NULL)
, m_pModeImageResourceHandler(NULL)
, m_pFillDrawContext(NULL)
, m_pFontAssetContainer(NULL)
, m_pFontAssetProcessor(NULL)
, m_pFontResourceHandler(NULL)
, m_pModeFontResourceHandler(NULL)
, m_pFontAssetBinary(NULL)
, m_pFontDrawContext(NULL)
, m_pSpriteDrawContext(NULL)
#if defined (WIN32) || defined(__APPLE__)
, m_pMouseReader(NULL)
#elif defined (__ANDROID__)
, m_pTouchReader(NULL)
#endif
, m_pCursor(NULL)
, m_pOverlay(NULL)
{
}

SpriteViewerProgram::~SpriteViewerProgram()
{
}

/*
 * Protected Instance
 */
void SpriteViewerProgram::InitFiles()
{
#if defined(WIN32)
	m_pAccessor = m_allocator.AllocType<lct::file::SystemAccessor>();
	m_pModeAccessor = m_allocator.AllocType<lct::file::SystemAccessor>();

	char currentDirectoryPath[1024];
	m_pModeAccessor->GetCurrentDirectoryPath(currentDirectoryPath, sizeof(currentDirectoryPath));
	LCT_TRACE("Current directory is: %s\n", currentDirectoryPath);
#elif defined(__ANDROID__)
	lct::file::PackedAccessor* pAccessor = m_allocator.AllocType<lct::file::PackedAccessor>();
	lct::file::PackedAccessor* pModeAccessor = m_allocator.AllocType<lct::file::PackedAccessor>();

	pAccessor->SetAssetManager(m_pAndroidApp->activity->assetManager);
	pModeAccessor->SetAssetManager(m_pAndroidApp->activity->assetManager);

	m_pAccessor = pAccessor;
	m_pModeAccessor = pModeAccessor;
#elif defined (__APPLE__)
    m_pAccessor = m_allocator.AllocType<lct::file::SystemAccessor>();
    m_pModeAccessor = m_allocator.AllocType<lct::file::SystemAccessor>();
    
    char currentDirectoryPath[1024];
    m_pModeAccessor->GetCurrentDirectoryPath(currentDirectoryPath, sizeof(currentDirectoryPath));
    LCT_TRACE("Current directory is: %s\n", currentDirectoryPath);
#endif

	m_pAccessor->SetAllocator(&m_allocator);
	m_pModeAccessor->SetAllocator(&m_modeAllocator);
}

void SpriteViewerProgram::InitWindow()
{
	Program::InitWindow();
}

void SpriteViewerProgram::InitAssets()
{
	// shader
	m_pShaderResourceHandler = m_allocator.AllocType<lct::shad::ResourceHandler>();
	m_pShaderResourceHandler->SetAllocator(&m_allocator);

	// image
	m_pImageAssetContainer = m_allocator.AllocType<lct::imag::AssetContainer>();
	m_pImageAssetContainer->SetAllocator(&m_allocator);

	m_pImageResourceHandler = m_allocator.AllocType<lct::imag::ResourceHandler>();
	m_pModeImageResourceHandler = m_allocator.AllocType<lct::imag::ResourceHandler>();

	m_pImageAssetProcessor = m_allocator.AllocType<lct::imag::AssetProcessor>();
	m_pImageAssetProcessor->SetAllocator(&m_allocator);
	m_pImageAssetProcessor->SetResourceHandler(m_pImageResourceHandler);
	m_pImageAssetProcessor->SetAssetContainer(m_pImageAssetContainer);

	// fill
	m_pFillDrawContext = m_allocator.AllocType<lct::fill::DrawContext>();
	m_pFillDrawContext->SetAllocator(&m_allocator);
	m_pFillDrawContext->SetShaderResourceHandler(m_pShaderResourceHandler);
	m_pFillDrawContext->CreateResources();

	LoadFillAssets();

	// font
	m_pFontAssetContainer = m_allocator.AllocType<lct::font::AssetContainer>();
	m_pFontAssetContainer->SetAllocator(&m_allocator);

	m_pFontAssetProcessor = m_allocator.AllocType<lct::font::AssetProcessor>();
	m_pFontAssetProcessor->SetAllocator(&m_allocator);
	m_pFontAssetProcessor->SetAssetContainer(m_pFontAssetContainer);

	m_pFontResourceHandler = m_allocator.AllocType<lct::font::ResourceHandler>();
	m_pModeFontResourceHandler = m_allocator.AllocType<lct::font::ResourceHandler>();

	m_pFontDrawContext = m_allocator.AllocType<lct::font::DrawContext>();
	m_pFontDrawContext->SetAllocator(&m_allocator);
	m_pFontDrawContext->SetShaderResourceHandler(m_pShaderResourceHandler);
	m_pFontDrawContext->CreateResources();

	LoadFontAssets();

	// sprite
	m_pSpriteDrawContext = m_allocator.AllocType<lct::spri::DrawContext>();
	m_pSpriteDrawContext->SetAllocator(&m_allocator);
	m_pSpriteDrawContext->SetShaderResourceHandler(m_pShaderResourceHandler);
	m_pSpriteDrawContext->CreateResources();

	LoadSpriteAssets();

	Program::InitAssets();
}

void SpriteViewerProgram::InitInput()
{
#if defined(WIN32) || defined(__APPLE__)
	m_pMouseReader = m_allocator.AllocType<lct::inpu::MouseReader>();

	lct::inpu::MouseCursor* pCursor = m_allocator.AllocType<lct::inpu::MouseCursor>();
	pCursor->SetReader(m_pMouseReader);

	m_pCursor = pCursor;
#elif defined(__ANDROID__)
	m_pTouchReader = m_allocator.AllocType<lct::inpu::TouchReader>();

	lct::inpu::TouchCursor* pCursor = m_allocator.AllocType<lct::inpu::TouchCursor>();
	pCursor->SetReader(m_pTouchReader);

	m_pCursor = pCursor;
#endif
}

void SpriteViewerProgram::InitModes()
{
	RegisterMode(lct::fram::ModeFactoryItem::CreateMode<HardcodedMode>, "HardcodedMode");
	RegisterMode(lct::fram::ModeFactoryItem::CreateMode<DataMode>, "DataMode");

	//m_modeTransition.SetNextModeNameString("HardcodedMode");
	m_pNextModeName = "DataMode";

	Program::InitModes();
}

void SpriteViewerProgram::InitOverlays()
{
	m_pOverlay = m_allocator.AllocType<SpriteViewerOverlay>();
	m_pOverlay->SetAllocator(&m_allocator);
	m_pOverlay->SetScreen(&m_screen);
	m_pOverlay->SetProgramMessageQueue(&m_messageQueue);
	m_pOverlay->SetInputCursor(m_pCursor);
	m_pOverlay->SetFillDrawContext(m_pFillDrawContext);
	m_pOverlay->SetFontAssetContainer(m_pFontAssetContainer);
	m_pOverlay->SetFontResourceHandler(m_pFontResourceHandler);
	m_pOverlay->SetFontDrawContext(m_pFontDrawContext);
	m_pOverlay->Init();

	if (m_graphicsAcquired)
	{
		m_pOverlay->AcquireGraphics();
	}

	Program::InitOverlays();
}

void SpriteViewerProgram::InitMessages()
{
	Program::InitMessages();
}

void SpriteViewerProgram::AcquireGraphics()
{
	Program::AcquireGraphics();

	m_pImageAssetProcessor->AcquireAllAssetResources();

	m_pFillDrawContext->AcquireResources();

	m_pFontDrawContext->AcquireResources();

	m_pSpriteDrawContext->AcquireResources();

	if (m_pOverlay != NULL) // should go in base...?
	{
		m_pOverlay->AcquireGraphics();
	}

	LCT_TRACE("SpriteViewerProgram::AcquireGraphics Image Texture Resource Count: %u\n", m_pImageResourceHandler->GetTextureResourceCount());
	LCT_TRACE("SpriteViewerProgram::AcquireGraphics Font Quad Resource Count: %u\n", m_pFontResourceHandler->GetQuadResourceCount());
}

void SpriteViewerProgram::ReleaseGraphics()
{
	m_pImageAssetProcessor->ReleaseAllAssetResources();

	m_pFillDrawContext->ReleaseResources();

	m_pFontDrawContext->ReleaseResources();

	m_pSpriteDrawContext->ReleaseResources();

	if (m_pOverlay != NULL) // should go in base...?
	{
		m_pOverlay->ReleaseGraphics();
	}

	LCT_TRACE("SpriteViewerProgram::ReleaseGraphics Image Texture Resource Count: %u\n", m_pImageResourceHandler->GetTextureResourceCount());
	LCT_TRACE("SpriteViewerProgram::ReleaseGraphics Font Quad Resource Count: %u\n", m_pFontResourceHandler->GetQuadResourceCount());

	Program::ReleaseGraphics();
}

void SpriteViewerProgram::ReadSystemMessages()
{
#if defined(WIN32) || defined(__APPLE__)
	m_pMouseReader->PrepareValues();
#elif defined(__ANDROID__)
	m_pTouchReader->PrepareValues();
#endif

	Program::ReadSystemMessages();
}

void SpriteViewerProgram::ReadInput()
{
	Program::ReadInput();

	CheckOverlayInput();
}

void SpriteViewerProgram::ConfigureMode()
{
	SpriteViewerMode* pSpriteViewerMode = static_cast<SpriteViewerMode*>(m_pCurrMode);

	pSpriteViewerMode->SetAccessor(m_pModeAccessor);
	pSpriteViewerMode->SetSpriteDrawContext(m_pSpriteDrawContext);
	pSpriteViewerMode->SetCursor(m_pCursor);
	pSpriteViewerMode->SetImageResourceHandler(m_pModeImageResourceHandler);
	pSpriteViewerMode->SetFillDrawContext(m_pFillDrawContext);
	pSpriteViewerMode->SetFontAssetContainer(m_pFontAssetContainer);
	pSpriteViewerMode->SetFontResourceHandler(m_pModeFontResourceHandler);
	pSpriteViewerMode->SetFontDrawContext(m_pFontDrawContext);

	Program::ConfigureMode();
}

bool SpriteViewerProgram::HandlePlatformMessage(const lct::foun::PlatformMessage& platformMessage)
{
#if defined(WIN32) || defined(__APPLE__)
	if (m_pMouseReader->HandlePlatformMessage(platformMessage))
	{
		return true;
	}
#elif defined(__ANDROID__)
	if (m_pTouchReader->HandlePlatformMessage(platformMessage))
	{
		return true;
	}
#endif
	else
	{
		return Program::HandlePlatformMessage(platformMessage);
	}
}

bool SpriteViewerProgram::HandleMessage(const lct::fram::Message& message)
{
	switch (message.GetType())
	{
	case MESSAGE_TYPE_PROGRAM_CLOSE_OVERLAY:
	{
		CloseOverlay();
		return true;
	}
	case MESSAGE_TYPE_PROGRAM_CHANGE_MODE:
	{
		const ProgramChangeModeContent* pContent = message.GetContent<ProgramChangeModeContent>();
		m_pNextModeName = pContent->pNextModeName;
		EndMode();
		BeginMode();
		return true;
	}
	default:
	{
		return false;
	}
	}
}

/*
 * Private Instance
 */
void SpriteViewerProgram::LoadFillAssets()
{
	 void* pVertexShaderBinary = NULL;
	 {
		 static const char* FILE_PATH = "data/shad/fillVertexShader.glsl";
		 u32 stringSize;
		 pVertexShaderBinary = m_pAccessor->LoadFileString(FILE_PATH, &stringSize);
	 }

	 void* pFragmentShaderBinary = NULL;
	 {
		 static const char* FILE_PATH = "data/shad/fillFragmentShader.glsl";
		 u32 stringSize;
		 pFragmentShaderBinary = m_pAccessor->LoadFileString(FILE_PATH, &stringSize);
	 }

	 m_pFillDrawContext->SetShaderBinaries(pVertexShaderBinary, pFragmentShaderBinary);
}

 void SpriteViewerProgram::LoadFontAssets()
 {
	 static const char* FILE_PATH = "data/font/package.bin";
	 u32 fileSize;
	 m_pFontAssetBinary = m_pAccessor->LoadFile(FILE_PATH, &fileSize);
	 lct::util::BinaryReader binaryReader;
	 binaryReader.SetMemory(m_pFontAssetBinary, fileSize);
	 lct::pack::PackageWalker packageWalker;
	 packageWalker.AddAssetHandler(m_pFontAssetProcessor);
	 packageWalker.AddAssetHandler(m_pImageAssetProcessor);

	 packageWalker.LoadAllAssets(binaryReader);

	 m_pFontAssetProcessor->FixupAllAssets(*m_pImageAssetContainer);

	 void* pVertexShaderBinary = NULL;
	 {
		 static const char* FILE_PATH = "data/shad/fontVertexShader.glsl";
		 u32 stringSize;
		 pVertexShaderBinary = m_pAccessor->LoadFileString(FILE_PATH, &stringSize);
	 }

	 void* pFragmentShaderBinary = NULL;
	 {
		 static const char* FILE_PATH = "data/shad/fontFragmentShader.glsl";
		 u32 stringSize;
		 pFragmentShaderBinary = m_pAccessor->LoadFileString(FILE_PATH, &stringSize);
	 }

	 m_pFontDrawContext->SetShaderBinaries(pVertexShaderBinary, pFragmentShaderBinary);
 }

 void SpriteViewerProgram::LoadSpriteAssets()
 {
	 void* pVertexShaderBinary = NULL;
	 {
		 static const char* FILE_PATH = "data/shad/spriteVertexShader.glsl";
		 u32 stringSize;
		 pVertexShaderBinary = m_pAccessor->LoadFileString(FILE_PATH, &stringSize);
	 }

	 void* pFragmentShaderBinary = NULL;
	 {
		 static const char* FILE_PATH = "data/shad/spriteFragmentShader.glsl";
		 u32 stringSize;
		 pFragmentShaderBinary = m_pAccessor->LoadFileString(FILE_PATH, &stringSize);
	 }

	 m_pSpriteDrawContext->SetShaderBinaries(pVertexShaderBinary, pFragmentShaderBinary);
 }

 void SpriteViewerProgram::CheckOverlayInput()
 {
	 if (m_pCurrOverlay == NULL)
	 {
		 s16 x = m_pCursor->GetX();
		 s16 y = m_pCursor->GetY();
		 if (m_pCursor->IsRelease() && (x < OVERLAY_TOUCH_ACTIVATE_SIZE) && (y < OVERLAY_TOUCH_ACTIVATE_SIZE))
		 {
			 m_pCurrOverlay = m_pOverlay;
		 }
	 }
 }

 void SpriteViewerProgram::OpenOverlay()
 {

 }

 void SpriteViewerProgram::CloseOverlay()
 {
	 m_pCurrOverlay = NULL;
 }
